<?php

class PlatformH5WeChat extends PlatformBase
{
    private $midasUrl;

    private $midasUri;

    private $midasOfferId;

    private $midasSecret;

    private $updateMoney;
    /**
     * @var PlatformH5WeChat
     */
    private static $instance;

    /**
     * Get the instance of PlatformH5WeChat.
     *
     * @return PlatformH5WeChat
     */
    public static function get()
    {
        if (!self::$instance) {
            self::$instance = new PlatformH5WeChat();
        }
        return self::$instance;
    }

    private function __construct()
    {
        $this->configs = SzConfig::get()->loadAppConfig(ModuleSns::getSnsConfigFileName(self::PLATFORM_H5_WE_CHAT), null, true);

        $this->appId = $this->configs['appId']['value'];
        $this->requestUrl = $this->configs['requestUrl']['value'];
        $this->appSecret = $this->configs['appSecret']['value'];
        $this->midasUrl = $this->configs['midasUrl']['value'];
        $this->midasUri = $this->configs['midasUri']['value'];
        $this->midasOfferId = $this->configs['midasOfferId']['value'];
        $this->midasSecret = $this->configs['midasSecret']['value'];
        $this->updateMoney = [];
    }

    /**
     * 获取session_key
     * @param $userId
     * @param $platformId
     * @return mixed|string
     */
    private function getSessionKey($userId, $platformId)
    {
        $redis = SzCacheFactory::get()->getAppCache($platformId);
        $sessionKey = $redis->get(GameConst::CACHE_KEY_WC_SESSION_KEY . $platformId);
        if ($sessionKey) {
            return $sessionKey;
        }
        $sessionId = UserService::get()->getPlatformToken($userId);
        $sessionKey = $this->saveSessionKey($platformId, $sessionId, $redis);
        return $sessionKey;
    }

    /**
     * 保存session_key
     * @param string $platformId
     * @param string $token
     * @param SzAbstractCache $redis
     * @return string
     */
    private function saveSessionKey($platformId, $token, $redis)
    {
        $reqTime = GameService::get()->getReqTime();
        $getParams = [
            'apptype' => 4,
            'methodCmd' => 1045,
            'appid' => $this->appId,
            'h5game_openid' => $platformId,
            'h5sdk_sessionid' => $token,
            'h5game_reqtime' => $reqTime,
            'h5game_sign' => md5($platformId . $reqTime . $this->appSecret),
            'gtkOption' => 1
        ];
        $url = $this->requestUrl . SzUtility::buildQueryString($getParams);
        $try = 5;
        $result = '';
        while (--$try >= 0) {
            try {
                $result = SzUtility::getHttpRequest($url, 20);
                $try = 0;
            } catch (SzException $e) {

            }
        }

        $result = json_decode($result, true);
        if (isset($result['iRet']) && $result['iRet'] == 0) {
            $redis->set(GameConst::CACHE_KEY_WC_SESSION_KEY . $platformId, $result['list']['session_key']);
            return $result['list']['session_key'];
        }
        return '';
    }

    /**
     * 验证token
     *
     * @param string $platformId 平台ID
     * @param string $token 登录token
     * @return bool
     */
    public function checkToken($platformId, $token)
    {
        $params = [
            'apptype' => 4,
            'methodCmd' => 1039,
            'appid' => $this->appId,
            'h5game_openid' => $platformId,
            'h5sdk_sessionid' => $token,
            'no_strong_check' => 1,
        ];
        $url = $this->requestUrl . SzUtility::buildQueryString($params);

        $try = 5;
        $result = '';
        while (--$try >= 0) {
            try {
                $result = SzUtility::getHttpRequest($url, 20);
                $try = 0;
            } catch (SzException $e) {

            }
        }

        $result = json_decode($result, true);
        if (isset($result['iRet']) && $result['iRet'] == 0) {
            $redis = SzCacheFactory::get()->getAppCache($platformId);
            $this->saveSessionKey($platformId, $token, $redis);
            return true;
        }
        return false;
    }

    /**
     *  微信小游戏上报游戏数据
     * http://h5sdk.qq.com/wiki/index.html?channel=wgame#pageName=wgameServer-methodCmd1043
     * @param int $userId
     * @param array (key => $value) $params
     * @return void
     */
    public function sendReportData($userId, $params)
    {
        $reqTime = GameService::get()->getReqTime();
        $platformId = UserService::get()->getPlatformId($userId, 'wc');
        $getParams = [
            'apptype' => 4,
            'methodCmd' => 1043,
            'appid' => $this->appId,
            'h5game_openid' => $platformId,
            'h5game_reqtime' => $reqTime,
            'h5game_sign' => md5($platformId . $reqTime . $this->appSecret),
        ];
        $url = $this->requestUrl . SzUtility::buildQueryString($getParams);

        $postParams = [];
        foreach ($params as $key => $value) {
            $postParams[] = ['key' => $key, 'value' => $value];
        }
        PlatformService::get()->postHttpRequest($url, json_encode($postParams), false, false, 60, false);
    }

    /**
     * 获取微信AccessToken
     * @param bool $bRefresh 是否强制刷新
     * @return string
     */
    private function getAccessToken($bRefresh = false)
    {
        if (!$bRefresh) {
            $accessToken = SzCacheFactory::get()->getAppCache()->get(GameConst::CACHE_KEY_WC_ACCESS_TOKEN);
            if ($accessToken) {
                return $accessToken;
            }
        }
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}";
        $result = SzUtility::getHttpRequest($url, 20);
        $result = json_decode($result, true);
        if (isset($result['access_token'])) {
            SzCacheFactory::get()->getAppCache()->set(GameConst::CACHE_KEY_WC_ACCESS_TOKEN, $result['access_token'], $result['expires_in'] - 1800);
            return $result['access_token'];
        }
        return '';
    }

    /**
     * 微信支付(米大师)相关请求
     * @param $uri
     * @param $accessToken
     * @param $sessionKey
     * @param $params
     * @param int $count
     * @return array
     * @throws SzException
     */
    private function midasHttpRequest($uri, $accessToken, $sessionKey, $params, $count = 5)
    {
        error_log('-------midasHttpRequest---$count===>'.$count."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
        if ($count <= 0) {
            throw new SzException(29200);
        }
        $url = $this->midasUrl . $uri . '?access_token=' . $accessToken;
        $result = PlatformService::get()->postHttpRequest($url, json_encode($params), false, false, 60, false);
        error_log('-------midasHttpRequest---result===>'.json_encode($result)."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
        error_log('-------midasHttpRequest---result[errcode]===>'.$result['errcode']."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
        SzLogger::get()->info('midasHttpRequest:: args:' . json_encode(func_get_args()) . '  result:' . json_encode($result) . '  count:' . $count);
        if (!isset($result['errcode']) || $result['errcode'] == -1) {//网络失败
            error_log('-------midasHttpRequest---网络失败造成--result[errcode]===>'.$result['errcode'].'------count===>'.$count."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
            return $this->midasHttpRequest($uri, $accessToken, $sessionKey, $params, --$count);
        } else if ($result['errcode'] == 0) {
            error_log('-------midasHttpRequest---扣款成功--result[errcode]===>'.$result['errcode'].'------count===>'.$count."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
            return $result;
        } else if ($result['errcode'] == 40001 || $result['errcode'] == 90009) {
            error_log('-------midasHttpRequest---签名错误mp_sig--result[errcode]===>'.$result['errcode'].'------count===>'.$count."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
            $accessToken = $this->getAccessToken(true);
            unset($params['access_token']);
            unset($params['mp_sig']);
            $params['access_token'] = $accessToken;
            $params['mp_sig'] = $this->getSign($params, $uri, 'session_key', $sessionKey);
            return $this->midasHttpRequest($uri, $accessToken, $sessionKey, $params, --$count);
        } else {
            throw new SzException(29201, [$uri, $result['errcode'], $result['errmsg'], json_encode(func_get_args())]);
        }
    }

    /**
     *  获取用户真实货币(同步米大师数据)
     * @param int $userId
     * @throws SzException
     * @return int
     */
    public function getMoney($userId)
    {
        $reqTime = SzTime::getTime();
        $platformId = UserService::get()->getPlatformId($userId, 'wc');
        $sessionKey = $this->getSessionKey($userId, $platformId);
        $accessToken = $this->getAccessToken();
        $uri = $this->midasUri . 'getbalance';
        $params = [
            'openid' => $platformId,
            'appid' => $this->appId,
            'offer_id' => $this->midasOfferId,
            'ts' => $reqTime,
            'zone_id' => '1',
            'pf' => 'android'
        ];
        $params['sig'] = $this->getSign($params, $uri, 'secret', $this->midasSecret);
        $params['access_token'] = $accessToken;
        $params['mp_sig'] = $this->getSign($params, $uri, 'session_key', $sessionKey);

        $result = $this->midasHttpRequest($uri, $accessToken, $sessionKey, $params);

        /**
         * @var ModulePaymentVo $payment
         */
        $payment = SzPersister::get()->getVo(null, ModulePaymentConst::ORM_NAME_PAYMENT, $userId);
        $payment->setBonusMoney($result['gen_balance']);
        $payment->setPurchasedMoney($result['balance'] - $result['gen_balance']);
        SzPersister::get()->setVo($payment);
        return $result['balance'];
    }

    /**
     *  向某个用户赠送游戏币
     * @param int $userId
     * @param array (key => $value) $params
     * @throws SzException
     * @return int
     */
    private function presentMoney($userId, $money)
    {
        $reqTime = SzTime::getTime();
        $platformId = UserService::get()->getPlatformId($userId, 'wc');
        $sessionKey = $this->getSessionKey($userId, $platformId);
        $accessToken = $this->getAccessToken();
        $uri = $this->midasUri . 'present';
        $params = [
            'openid' => $platformId,
            'appid' => $this->appId,
            'offer_id' => $this->midasOfferId,
            'ts' => $reqTime,
            'zone_id' => '1',
            'pf' => 'android',
            'bill_no' => $this->genBillNo($userId, $reqTime),
            'present_counts' => $money
        ];
        $params['sig'] = $this->getSign($params, $uri, 'secret', $this->midasSecret);
        $params['access_token'] = $accessToken;
        $params['mp_sig'] = $this->getSign($params, $uri, 'session_key', $sessionKey);

        return $this->midasHttpRequest($uri, $accessToken, $sessionKey, $params);
    }

    /**
     * 扣除某个用户的游戏币
     * @param int $userId
     * @param $money
     * @return array
     * @throws SzException
     */
    private function payMoney($userId, $money)
    {
        $reqTime = SzTime::getTime();
        $platformId = UserService::get()->getPlatformId($userId, 'wc');
        $sessionKey = $this->getSessionKey($userId, $platformId);
        $accessToken = $this->getAccessToken();
        $uri = $this->midasUri . 'pay';
        $params = [
            'openid' => $platformId,
            'appid' => $this->appId,
            'offer_id' => $this->midasOfferId,
            'ts' => $reqTime,
            'zone_id' => '1',
            'pf' => 'android',
            'amt' => $money,
            'bill_no' => $this->genBillNo($userId, $reqTime)
        ];
        $params['sig'] = $this->getSign($params, $uri, 'secret', $this->midasSecret);
        $params['access_token'] = $accessToken;
        $params['mp_sig'] = $this->getSign($params, $uri, 'session_key', $sessionKey);
        error_log('-------payMoney---params===>'.json_encode($params)."\r\n",3,'D:/phpstudy_pro/log/recharge.log');

        return $this->midasHttpRequest($uri, $accessToken, $sessionKey, $params);
    }

    /**
     * 获取支付签名
     * @param $params
     * @param $uri
     * @param $secretKey
     * @param $secretValue
     * @return string
     */
    private function getSign($params, $uri, $secretKey, $secretValue)
    {
        ksort($params);
        $sign = SzUtility::buildQueryString($params, false);
        $sign .= '&org_loc=' . $uri . '&method=POST&' . $secretKey . '=' . $secretValue;
        return hash_hmac('sha256', $sign, $secretValue);
    }

    /**
     * 生成流水号
     * @param int $userId
     * @param int $time
     * @return string
     */
    private function genBillNo($userId, $time)
    {
        return SzUtility::genMd5(uniqid($userId . '_' . $time, false));
    }

    /**
     * 修改钻石
     * 微信平台钻石是托管在微信平台的，每次修改钻石都需要通知微信平台
     * 为避免钻石回滚操作，此接口只记录需要修改的数量
     * 在接口执行完通过postHandler调用syncMoney通知微信平台修改钻石
     *
     * @param int $userId
     * @param int $money
     * @param ModulePaymentVo $payment default null
     * @param boolean $logCommodityConsume default true, 如果true，记为价值消耗
     * @param int $commodityValue default 0
     * @return array(ModulePaymentVo, int, array($entityId => array($count, $commodityValue), ...))
     */
    public function updateMoney($userId, $money, $payment = null, $logCommodityConsume = true, $commodityValue = 0)
    {
        if (isset($this->updateMoney[$userId])) {
            $this->updateMoney[$userId] += $money;
        } else {
            $this->updateMoney[$userId] = $money;
        }
        return array(null, 0, []);
    }

    /**
     * 统一同步钻石
     * @return void
     */
    public function syncMoney()
    {
        /**
         * @var ModulePaymentVo $payment
         */
        foreach ($this->updateMoney as $userId => $money) {
            if (0 == $money) {
                continue;
            }
            $money = ceil($money);
            $payment = SzPersister::get()->getVo(null, ModulePaymentConst::ORM_NAME_PAYMENT, $userId);
            if ($money < 0) {
                $result = $this->payMoney($userId, abs($money));

                $bonusMoney = $payment->getBonusMoney();

                $bonusMoney -= $result['used_gen_amt'];
                $bonusMoney > 0 ?: $bonusMoney = 0;

                $purchasedMoney = $result['balance'] - $bonusMoney;
                $payment->setBonusMoney($bonusMoney);
                $payment->setPurchasedMoney($purchasedMoney);
                SzPersister::get()->setVo($payment);
            } else if ($money > 0) {
                $result = $this->presentMoney($userId, abs($money));

                $bonusMoney = $payment->getBonusMoney();

                $bonusMoney += abs($money);
                $purchasedMoney = $result['balance'] - $bonusMoney;
                $payment->setBonusMoney($bonusMoney);
                $payment->setPurchasedMoney($purchasedMoney);
                SzPersister::get()->setVo($payment);
            }
        }
    }


    /**
     *  获取用户米大师余额数量
     * @param int $userId
     * @throws SzException
     * @return int
     */
    public function getAmount($userId)
    {
        $reqTime = SzTime::getTime();
        $platformId = UserService::get()->getPlatformId($userId, 'wc');
        $sessionKey = $this->getSessionKey($userId, $platformId);
        $accessToken = $this->getAccessToken();
        $uri = $this->midasUri . 'getbalance';
        $params = [
            'openid' => $platformId,
            'appid' => $this->appId,
            'offer_id' => $this->midasOfferId,
            'ts' => $reqTime,
            'zone_id' => '1',
            'pf' => 'android'
        ];
        $params['sig'] = $this->getSign($params, $uri, 'secret', $this->midasSecret);
        $params['access_token'] = $accessToken;
        $params['mp_sig'] = $this->getSign($params, $uri, 'session_key', $sessionKey);

        $result = $this->midasHttpRequest($uri, $accessToken, $sessionKey, $params);

        return $result['balance'] - $result['gen_balance'];
    }

    /**
     * 安全调用付款
     * @param $userId
     * @param $money
     * @return array
     * @throws SzException
     */
    public function safePayment($userId,$money)
    {
        error_log('-------safePayment===>'."\r\n",3,'D:/phpstudy_pro/log/recharge.log');
        $res = $this->payMoney($userId,$money);
        return $res;
    }

}